#pragma once
#include "../source/message.hpp"
#include "../source/net.hpp"
#include "Requestor.hpp"
#include "./Rpc_registry.hpp"
#include "../server/Rpc_registry.hpp"
#include "../source/dispacher.hpp"
#include "Rpc_call.hpp"
#include "Rpc_topic.hpp"

namespace ns_rpc_client
{
    class RegisteryClient
    {
    public:
        //构造时，传入注册中心的地址信息，用于连接注册中心
        using ptr = std::shared_ptr<RegisteryClient>;
        RegisteryClient(const std::string& ip,int port)
        :_requestor(std::make_shared<ns_Req::Requestor>())
        ,_dispacher(std::make_shared<ns_dispacher::Dispacher>())
        ,_provider(std::make_shared<ns_client_registry::Provider>(_requestor))
        {
            auto msg_rsp = std::bind(&ns_Req::Requestor::OnResponse,_requestor.get(),std::placeholders::_1,std::placeholders::_2);
            _dispacher->Registercb<BaseMessage>(Mtype::SERVICE_RES,msg_rsp);

            auto msg_req = std::bind(&ns_dispacher::Dispacher::OnMessage,_dispacher.get(),std::placeholders::_1,std::placeholders::_2);
            _client = ns_net::ClientFactory::ClientCreate(ip,port);
            _client->SetMessageCallback(msg_req);
            _client->connect();
        }
        //向外提供服务注册接口
        bool RegisterService(const std::string& method,const Address& host)
        {
            return _provider->RegistryMethod(_client->connection(),host,method);
        }
    private:
        ns_Req::Requestor::ptr _requestor;
        ns_client_registry::Provider::ptr _provider;
        ns_dispacher::Dispacher::ptr _dispacher;
        BaseClient::ptr _client;
    };

    class DiscovererClient
    {
    public:
        using ptr = std::shared_ptr<DiscovererClient>;
        DiscovererClient(const std::string& ip,int port,const ns_client_registry::Discoverer::Offlinecb& offlinecb)
        :_requestor(std::make_shared<ns_Req::Requestor>())
        ,_dispacher(std::make_shared<ns_dispacher::Dispacher>())
        ,_discoverer(std::make_shared<ns_client_registry::Discoverer>(_requestor,offlinecb))
        ,_offlinecb(offlinecb)
        {
            auto msg_rsp = std::bind(&ns_Req::Requestor::OnResponse,_requestor.get(),std::placeholders::_1,std::placeholders::_2);
            _dispacher->Registercb<BaseMessage>(Mtype::SERVICE_RES,msg_rsp);
            //discoverer多了服务上线下线请求处理的回调
            auto msg_req = std::bind(&ns_client_registry::Discoverer::OnServiceRequest,_discoverer.get(),std::placeholders::_1,std::placeholders::_2);
            _dispacher->Registercb<ServiceRequest>(Mtype::SERVICE_REQ,msg_req);

            auto cb = std::bind(&ns_dispacher::Dispacher::OnMessage,_dispacher.get(),std::placeholders::_1,std::placeholders::_2);
            _client = ns_net::ClientFactory::ClientCreate(ip,port);
            _client->SetMessageCallback(cb);
            _client->connect();
        }
        //向外提供服务发现接口
        bool DiscoveryService(const std::string& method,Address& host)
        {
            return _discoverer->ServiceDiscover(_client->connection(),method,host);
        }
    private:
        ns_client_registry::Discoverer::Offlinecb _offlinecb;
        ns_client_registry::Discoverer::ptr _discoverer;
        ns_dispacher::Dispacher::ptr _dispacher;
        ns_Req::Requestor::ptr _requestor;
        BaseClient::ptr _client;
    };

    class RpcClient
    {
    public:
        using ptr = std::shared_ptr<RpcClient>;
        //如果enableDiscover为假，那就是服务提供者的主机信息，如果为真，就是服务注册中心的主机信息
        RpcClient(bool enableDiscover,const std::string& ip,int port)
        :_requestor(std::make_shared<ns_Req::Requestor>())
        ,_enableDiscover(enableDiscover)
        ,_dispacher(std::make_shared<ns_dispacher::Dispacher>())
        ,_caller(std::make_shared<ns_rpc_call::RpcCaller>(_requestor))
        {
            auto rsp_cb = std::bind(&ns_Req::Requestor::OnResponse, _requestor.get(), 
                        std::placeholders::_1, std::placeholders::_2);
                    _dispacher->Registercb<BaseMessage>(Mtype::RPC_RES,rsp_cb);
            if(_enableDiscover)
            {
                //启用服务发现客户端，地址是注册中心的地址
                auto cb = std::bind(&RpcClient::DelClient,this,std::placeholders::_1);
                _discoverer_client = std::make_shared<ns_rpc_client::DiscovererClient>(ip,port,cb);
            }
            else
            {
                auto cb = std::bind(&ns_dispacher::Dispacher::OnMessage,_dispacher.get(),std::placeholders::_1,std::placeholders::_2);
                _rpc_client = ns_net::ClientFactory::ClientCreate(ip,port);
                _rpc_client->SetMessageCallback(cb);
                _rpc_client->connect();
            }
        }
        bool Call(const std::string &method_name, const Json::Value& paras, Json::Value& result)
        {
            BaseClient::ptr client;
            //1.服务发现客户端，先去连接池看看有没有已经发现过的客户端，有则赋，无则创建
            if(_enableDiscover)
            {   
                Address host;
                //通过服务发现，获取服务提供者的地址信息
                bool ret = _discoverer_client->DiscoveryService(method_name,host);
                if(ret == false)   
                {
                    LOG(ERROR,"没有找到该服务提供者,%s: ",method_name.c_str());
                    return false;
                }
                //2.根据地址信息，查找连接池里有没有创建好的客户端，没有就构造一个
                client = GetClient(host);
                if(client.get() == nullptr)
                    client = NewClient(host);
            }
            else
                client = _rpc_client;
            //3.通过客户端连接发送Rpc请求
            return _caller->Call(client->connection(),method_name,paras,result);
            
        }
        bool Call(const std::string &method_name, const Json::Value& paras, ns_rpc_call::RpcCaller::JsonAsyncResponse& result)
        {
            BaseClient::ptr client;
            //1.服务发现客户端，先去连接池看看有没有已经发现过的客户端，有则赋，无则创建
            if(_enableDiscover)
            {   
                Address host;
                //通过服务发现，获取服务提供者的地址信息
                bool ret = _discoverer_client->DiscoveryService(method_name,host);
                if(ret == false)   
                {
                    LOG(ERROR,"没有找到该服务提供者,%s: ",method_name.c_str());
                    return false;
                }
                //2.根据地址信息，查找连接池里有没有创建好的客户端，没有就构造一个
                client = GetClient(host);
                if(client.get() == nullptr)
                    client = NewClient(host);
            }
            else
                client = _rpc_client;
            //3.通过客户端连接发送Rpc请求
            return _caller->Call(client->connection(),method_name,paras,result);
        }
        bool Call(const std::string &method_name,const Json::Value& paras,const ns_rpc_call::RpcCaller::JsonResponseCallback& cb)
        {
            BaseClient::ptr client;
            //1.服务发现客户端，先去连接池看看有没有已经发现过的客户端，有则赋，无则创建
            if(_enableDiscover)
            {   
                Address host;
                //通过服务发现，获取服务提供者的地址信息
                bool ret = _discoverer_client->DiscoveryService(method_name,host);
                if(ret == false)   
                {
                    LOG(ERROR,"没有找到该服务提供者,%s: ",method_name.c_str());
                    return false;
                }
                //2.根据地址信息，查找连接池里有没有创建好的客户端，没有就构造一个
                client = GetClient(host);
                if(client.get() == nullptr)
                    client = NewClient(host);
            }
            else
                client = _rpc_client;
            //3.通过客户端连接发送Rpc请求
            return _caller->Call(client->connection(),method_name,paras,cb);
        }
    private:
        BaseClient::ptr NewClient(const Address& host)
        {
            auto msg_cb = std::bind(&ns_dispacher::Dispacher::OnMessage,_dispacher.get(),std::placeholders::_1,std::placeholders::_2);
            auto client = ns_net::ClientFactory::ClientCreate(host.first,host.second);
            client->SetMessageCallback(msg_cb);
            client->connect();
            SetClient(host,client);
            return client;
        }
        BaseClient::ptr GetClient(const Address& host)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _rpc_clients.find(host);
            if(it == _rpc_clients.end())
                return BaseClient::ptr();
            return it->second;
        }
        void SetClient(const Address& host,BaseClient::ptr client)
        {   
            std::unique_lock<std::mutex> lock(_mutex);
            _rpc_clients.insert(std::make_pair(host,client));
        }
        void DelClient(const Address& host)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _rpc_clients.erase(host);
        }
        struct AddressHash
        {
            size_t operator()(const Address& addr) const
            {
                std::string ret = addr.first + std::to_string(addr.second);
                return std::hash<std::string>{}(ret);
            }
        };
        std::mutex _mutex;
        bool _enableDiscover;
        DiscovererClient::ptr _discoverer_client;
        ns_dispacher::Dispacher::ptr _dispacher;
        ns_Req::Requestor::ptr _requestor;
        ns_rpc_call::RpcCaller::ptr _caller;
        BaseClient::ptr _rpc_client; //不进行服务发现时的客户端
        std::unordered_map<Address,BaseClient::ptr,AddressHash> _rpc_clients; //连接池
    };



    class TopicClient
    {
    public:
        //构造时，传入注册中心的地址信息，用于连接注册中心
        using ptr = std::shared_ptr<RegisteryClient>;
        TopicClient(const std::string& ip,int port)
        :_requestor(std::make_shared<ns_Req::Requestor>())
        ,_dispacher(std::make_shared<ns_dispacher::Dispacher>())
        ,_topic_manager(std::make_shared<ns_client_topic::TopicManager>(_requestor))
        {
            auto topic_res = std::bind(&ns_Req::Requestor::OnResponse,_requestor.get(),std::placeholders::_1,std::placeholders::_2);
            _dispacher->Registercb<BaseMessage>(Mtype::TOPIC_RES,topic_res);

            auto topic_req = std::bind(&ns_client_topic::TopicManager::OnPublish,_topic_manager.get(),std::placeholders::_1,std::placeholders::_2);
            _dispacher->Registercb<TopicRequest>(Mtype::TOPIC_REQ,topic_req);

            auto msg_req = std::bind(&ns_dispacher::Dispacher::OnMessage,_dispacher.get(),std::placeholders::_1,std::placeholders::_2);
            _client = ns_net::ClientFactory::ClientCreate(ip,port);
            _client->SetMessageCallback(msg_req);
            _client->connect();
        }
        bool Create(const std::string& topic_name)
        {
            return _topic_manager->Create(_client->connection(),topic_name);
        }
        bool Remove(const std::string& topic_name)
        {
            return _topic_manager->Remove(_client->connection(),topic_name);
        }
        bool Subscribe(const std::string& topic_name,const ns_client_topic::TopicManager::subcallback& scb)
        {
            return _topic_manager->Subscribe(_client->connection(),topic_name,scb);
        }
        bool Cancel(const std::string& topic_name)
        {
            return _topic_manager->Cancel(_client->connection(),topic_name);
        }
        bool Publish(const std::string& topic_name,const std::string& msg)
        {
            return _topic_manager->Publish(_client->connection(),topic_name,msg);
        }
    private:
        ns_Req::Requestor::ptr _requestor;
        ns_client_topic::TopicManager::ptr _topic_manager;
        ns_dispacher::Dispacher::ptr _dispacher;
        BaseClient::ptr _client;
    };
}